{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "2d251fac-b9d7-497a-ad62-bc61c7755b5c",
   "metadata": {},
   "source": [
    "- https://github.com/langchain-ai/langgraph/blob/main/examples/multi_agent/agent_supervisor.ipynb"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "2f2fa2b5-0061-4072-b731-d19e8e0b4e6e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:19:27.906574Z",
     "iopub.status.busy": "2024-09-11T15:19:27.905959Z",
     "iopub.status.idle": "2024-09-11T15:19:27.915578Z",
     "shell.execute_reply": "2024-09-11T15:19:27.913487Z",
     "shell.execute_reply.started": "2024-09-11T15:19:27.906515Z"
    }
   },
   "outputs": [],
   "source": [
    "from IPython.display import Image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "1e0f1975-12d5-4515-93d0-e5736ac9fc2d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:19:36.157443Z",
     "iopub.status.busy": "2024-09-11T15:19:36.157075Z",
     "iopub.status.idle": "2024-09-11T15:19:36.171055Z",
     "shell.execute_reply": "2024-09-11T15:19:36.169076Z",
     "shell.execute_reply.started": "2024-09-11T15:19:36.157412Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<img src=\"https://raw.githubusercontent.com/langchain-ai/langgraph/f3b5d804ab72a1e7485dbc172ea73957efb268c1/examples/multi_agent/img/supervisor-diagram.png\" width=\"500\"/>"
      ],
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Image(url='https://raw.githubusercontent.com/langchain-ai/langgraph/f3b5d804ab72a1e7485dbc172ea73957efb268c1/examples/multi_agent/img/supervisor-diagram.png', width=500)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dffe6a47-7ab4-4699-872e-70597171fe1d",
   "metadata": {},
   "source": [
    "- multi agents\n",
    "    - as a team/group\n",
    "- 三条边指向 supervisor\n",
    "    - `{agent1, agent2, agent3} => supervisor`\n",
    "    ```\n",
    "    for agent in ['agent 1', 'agent 2', 'agent 3']:\n",
    "        # We want our workers to ALWAYS \"report back\" to the supervisor when done\n",
    "        workflow.add_edge(agent, \"supervisor\")\n",
    "    ```\n",
    "- share state\n",
    "    - global state of the graph\n",
    "- message type\n",
    "    - langchain_core.messages.ai.AIMessage,\n",
    "    - langchain_core.messages.human.HumanMessage,\n",
    "    - langchain_core.messages.chat.ChatMessage,\n",
    "    - langchain_core.messages.system.SystemMessage,\n",
    "    - langchain_core.messages.function.FunctionMessage,\n",
    "    - langchain_core.messages.tool.ToolMessage"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "c0de5649-d036-4b09-8051-347dc0e77986",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:20:40.927754Z",
     "iopub.status.busy": "2024-09-11T15:20:40.927132Z",
     "iopub.status.idle": "2024-09-11T15:20:40.939512Z",
     "shell.execute_reply": "2024-09-11T15:20:40.937614Z",
     "shell.execute_reply.started": "2024-09-11T15:20:40.927697Z"
    }
   },
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "assert load_dotenv()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "55bc9a98-fe35-4064-8595-1f4151fc06a9",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:24:01.395748Z",
     "iopub.status.busy": "2024-09-11T15:24:01.395152Z",
     "iopub.status.idle": "2024-09-11T15:24:02.045956Z",
     "shell.execute_reply": "2024-09-11T15:24:02.044837Z",
     "shell.execute_reply.started": "2024-09-11T15:24:01.395694Z"
    }
   },
   "outputs": [],
   "source": [
    "from typing import Annotated\n",
    "\n",
    "from langchain_community.tools.tavily_search import TavilySearchResults\n",
    "from langchain_experimental.tools import PythonREPLTool\n",
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
    "from langchain_openai import ChatOpenAI\n",
    "from pydantic import BaseModel\n",
    "from typing import Literal"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ee5c4485-117f-4eda-bbb2-b651d5794e44",
   "metadata": {},
   "source": [
    "### tools"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "b474c376-893d-40d2-ac8d-833f992db072",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:21:13.282241Z",
     "iopub.status.busy": "2024-09-11T15:21:13.281532Z",
     "iopub.status.idle": "2024-09-11T15:21:13.291841Z",
     "shell.execute_reply": "2024-09-11T15:21:13.289928Z",
     "shell.execute_reply.started": "2024-09-11T15:21:13.282188Z"
    }
   },
   "outputs": [],
   "source": [
    "tavily_tool = TavilySearchResults(max_results=5)\n",
    "# This executes code locally, which can be unsafe\n",
    "python_repl_tool = PythonREPLTool()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "264ff956-fc28-4c69-9946-cda7d2fcba60",
   "metadata": {},
   "source": [
    "### helper utilities"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "31e1e7f5-54aa-4523-8d21-fd240a22ff49",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:23:44.209832Z",
     "iopub.status.busy": "2024-09-11T15:23:44.209163Z",
     "iopub.status.idle": "2024-09-11T15:23:44.218862Z",
     "shell.execute_reply": "2024-09-11T15:23:44.216531Z",
     "shell.execute_reply.started": "2024-09-11T15:23:44.209774Z"
    }
   },
   "outputs": [],
   "source": [
    "# convert the agent response to a human message\n",
    "def agent_node(state, agent, name):\n",
    "    result = agent.invoke(state)\n",
    "    return {\"messages\": [HumanMessage(content=result[\"messages\"][-1].content, name=name)]}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8e4421ef-9060-4996-9a02-eeb08b83235d",
   "metadata": {},
   "source": [
    "### agent supervisor (node of the graph)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "8cab4815-5b32-48c6-a24c-3da87199a828",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:28:25.589646Z",
     "iopub.status.busy": "2024-09-11T15:28:25.588861Z",
     "iopub.status.idle": "2024-09-11T15:28:25.595494Z",
     "shell.execute_reply": "2024-09-11T15:28:25.594601Z",
     "shell.execute_reply.started": "2024-09-11T15:28:25.589590Z"
    }
   },
   "outputs": [],
   "source": [
    "members = [\"Researcher\", \"Coder\"]\n",
    "#  variables: members\n",
    "system_prompt = (\n",
    "    \"You are a supervisor tasked with managing a conversation between the\"\n",
    "    \" following workers:  {members}. Given the following user request,\"\n",
    "    \" respond with the worker to act next. Each worker will perform a\"\n",
    "    \" task and respond with their results and status. When finished,\"\n",
    "    \" respond with FINISH.\"\n",
    ")\n",
    "\n",
    "# Our team supervisor is an LLM node. It just picks the next agent to process\n",
    "# and decides when the work is completed\n",
    "options = [\"FINISH\"] + members"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "4d27385c-f419-477e-981e-064c697241bd",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:29:44.275473Z",
     "iopub.status.busy": "2024-09-11T15:29:44.274852Z",
     "iopub.status.idle": "2024-09-11T15:29:44.284271Z",
     "shell.execute_reply": "2024-09-11T15:29:44.283208Z",
     "shell.execute_reply.started": "2024-09-11T15:29:44.275421Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"['FINISH', 'Researcher', 'Coder']\""
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "str(options)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "d2e51836-551c-4562-8da5-179f57fd4d98",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:28:33.374331Z",
     "iopub.status.busy": "2024-09-11T15:28:33.373658Z",
     "iopub.status.idle": "2024-09-11T15:28:33.383908Z",
     "shell.execute_reply": "2024-09-11T15:28:33.382779Z",
     "shell.execute_reply.started": "2024-09-11T15:28:33.374276Z"
    }
   },
   "outputs": [],
   "source": [
    "class RouteResponse(BaseModel):\n",
    "    next: Literal[*options]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "e638fcc1-1211-410c-b493-81930bd66f04",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:29:59.178935Z",
     "iopub.status.busy": "2024-09-11T15:29:59.178346Z",
     "iopub.status.idle": "2024-09-11T15:29:59.187751Z",
     "shell.execute_reply": "2024-09-11T15:29:59.186637Z",
     "shell.execute_reply.started": "2024-09-11T15:29:59.178884Z"
    }
   },
   "outputs": [],
   "source": [
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\"system\", system_prompt),\n",
    "        MessagesPlaceholder(variable_name=\"messages\"),\n",
    "        (\n",
    "            \"system\",\n",
    "            \"Given the conversation above, who should act next?\"\n",
    "            \" Or should we FINISH? Select one of: {options}\",\n",
    "        ),\n",
    "    ]\n",
    ").partial(options=str(options), members=\", \".join(members))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "f4fb76a5-04f7-4c1d-be4d-1803c83078a9",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:30:05.636600Z",
     "iopub.status.busy": "2024-09-11T15:30:05.636013Z",
     "iopub.status.idle": "2024-09-11T15:30:05.645963Z",
     "shell.execute_reply": "2024-09-11T15:30:05.644055Z",
     "shell.execute_reply.started": "2024-09-11T15:30:05.636565Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m System Message \u001b[0m================================\n",
      "\n",
      "You are a supervisor tasked with managing a conversation between the following workers:  \u001b[33;1m\u001b[1;3m{members}\u001b[0m. Given the following user request, respond with the worker to act next. Each worker will perform a task and respond with their results and status. When finished, respond with FINISH.\n",
      "\n",
      "=============================\u001b[1m Messages Placeholder \u001b[0m=============================\n",
      "\n",
      "\u001b[33;1m\u001b[1;3m{messages}\u001b[0m\n",
      "\n",
      "================================\u001b[1m System Message \u001b[0m================================\n",
      "\n",
      "Given the conversation above, who should act next? Or should we FINISH? Select one of: \u001b[33;1m\u001b[1;3m{options}\u001b[0m\n"
     ]
    }
   ],
   "source": [
    "prompt.pretty_print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "3dd99e0a-f331-434e-a565-6a20c702756f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:30:27.948483Z",
     "iopub.status.busy": "2024-09-11T15:30:27.947867Z",
     "iopub.status.idle": "2024-09-11T15:30:27.959022Z",
     "shell.execute_reply": "2024-09-11T15:30:27.957955Z",
     "shell.execute_reply.started": "2024-09-11T15:30:27.948430Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ChatPromptTemplate(input_variables=['messages'], input_types={'messages': typing.List[typing.Union[langchain_core.messages.ai.AIMessage, langchain_core.messages.human.HumanMessage, langchain_core.messages.chat.ChatMessage, langchain_core.messages.system.SystemMessage, langchain_core.messages.function.FunctionMessage, langchain_core.messages.tool.ToolMessage]]}, partial_variables={'options': \"['FINISH', 'Researcher', 'Coder']\", 'members': 'Researcher, Coder'}, messages=[SystemMessagePromptTemplate(prompt=PromptTemplate(input_variables=['members'], template='You are a supervisor tasked with managing a conversation between the following workers:  {members}. Given the following user request, respond with the worker to act next. Each worker will perform a task and respond with their results and status. When finished, respond with FINISH.')), MessagesPlaceholder(variable_name='messages'), SystemMessagePromptTemplate(prompt=PromptTemplate(input_variables=['options'], template='Given the conversation above, who should act next? Or should we FINISH? Select one of: {options}'))])"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prompt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "7f05bd64-e5ed-44de-acc2-5723515ff6d6",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:32:00.501765Z",
     "iopub.status.busy": "2024-09-11T15:32:00.501094Z",
     "iopub.status.idle": "2024-09-11T15:32:00.607432Z",
     "shell.execute_reply": "2024-09-11T15:32:00.606234Z",
     "shell.execute_reply.started": "2024-09-11T15:32:00.501710Z"
    }
   },
   "outputs": [],
   "source": [
    "llm = ChatOpenAI(model=\"gpt-4o\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "c5ce8afe-ba84-42cf-9774-9299e91c8a41",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:32:30.366043Z",
     "iopub.status.busy": "2024-09-11T15:32:30.365413Z",
     "iopub.status.idle": "2024-09-11T15:32:30.374367Z",
     "shell.execute_reply": "2024-09-11T15:32:30.372496Z",
     "shell.execute_reply.started": "2024-09-11T15:32:30.365990Z"
    }
   },
   "outputs": [],
   "source": [
    "def supervisor_agent(state):\n",
    "    supervisor_chain = (\n",
    "        prompt\n",
    "        | llm.with_structured_output(RouteResponse)\n",
    "    )\n",
    "    return supervisor_chain.invoke(state)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "64167689-9d50-47cc-b12f-957a4cd3dcc1",
   "metadata": {},
   "source": [
    "### nodes & graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "52fa3ef8-6a27-485e-b719-d8fffb7171eb",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:35:30.536665Z",
     "iopub.status.busy": "2024-09-11T15:35:30.536063Z",
     "iopub.status.idle": "2024-09-11T15:35:30.624661Z",
     "shell.execute_reply": "2024-09-11T15:35:30.623504Z",
     "shell.execute_reply.started": "2024-09-11T15:35:30.536613Z"
    }
   },
   "outputs": [],
   "source": [
    "import functools\n",
    "import operator\n",
    "from typing import Sequence, TypedDict\n",
    "\n",
    "from langchain_core.messages import BaseMessage\n",
    "\n",
    "from langgraph.graph import END, StateGraph, START\n",
    "from langgraph.prebuilt import create_react_agent"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "1c894331-16c5-4705-95ad-5c161e775796",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:35:38.632826Z",
     "iopub.status.busy": "2024-09-11T15:35:38.632328Z",
     "iopub.status.idle": "2024-09-11T15:35:38.641477Z",
     "shell.execute_reply": "2024-09-11T15:35:38.639576Z",
     "shell.execute_reply.started": "2024-09-11T15:35:38.632784Z"
    }
   },
   "outputs": [],
   "source": [
    "# The agent state is the input to each node in the graph\n",
    "class AgentState(TypedDict):\n",
    "    # The annotation tells the graph that new messages will always\n",
    "    # be added to the current states\n",
    "    messages: Annotated[Sequence[BaseMessage], operator.add]\n",
    "    # The 'next' field indicates where to route to next\n",
    "    next: str"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "f2788dfe-5980-4ded-8b62-e5785848217b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:35:45.835704Z",
     "iopub.status.busy": "2024-09-11T15:35:45.834947Z",
     "iopub.status.idle": "2024-09-11T15:35:45.983805Z",
     "shell.execute_reply": "2024-09-11T15:35:45.982078Z",
     "shell.execute_reply.started": "2024-09-11T15:35:45.835651Z"
    }
   },
   "outputs": [],
   "source": [
    "research_agent = create_react_agent(llm, tools=[tavily_tool])\n",
    "research_node = functools.partial(agent_node, agent=research_agent, name=\"Researcher\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "43ffdcf5-46cc-4823-b27c-d73084533a41",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:35:54.721176Z",
     "iopub.status.busy": "2024-09-11T15:35:54.720817Z",
     "iopub.status.idle": "2024-09-11T15:35:54.742691Z",
     "shell.execute_reply": "2024-09-11T15:35:54.740773Z",
     "shell.execute_reply.started": "2024-09-11T15:35:54.721148Z"
    }
   },
   "outputs": [],
   "source": [
    "code_agent = create_react_agent(llm, tools=[python_repl_tool])\n",
    "code_node = functools.partial(agent_node, agent=code_agent, name=\"Coder\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "0b304768-8423-44b0-9ec5-a7193c4af7d5",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:36:04.446020Z",
     "iopub.status.busy": "2024-09-11T15:36:04.445369Z",
     "iopub.status.idle": "2024-09-11T15:36:04.457222Z",
     "shell.execute_reply": "2024-09-11T15:36:04.455135Z",
     "shell.execute_reply.started": "2024-09-11T15:36:04.445967Z"
    }
   },
   "outputs": [],
   "source": [
    "workflow = StateGraph(AgentState)\n",
    "workflow.add_node(\"Researcher\", research_node)\n",
    "workflow.add_node(\"Coder\", code_node)\n",
    "workflow.add_node(\"supervisor\", supervisor_agent)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "c96e3bda-4249-4fb1-aa0f-0597c0f0fb17",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:36:22.108393Z",
     "iopub.status.busy": "2024-09-11T15:36:22.107767Z",
     "iopub.status.idle": "2024-09-11T15:36:22.116852Z",
     "shell.execute_reply": "2024-09-11T15:36:22.114693Z",
     "shell.execute_reply.started": "2024-09-11T15:36:22.108340Z"
    }
   },
   "outputs": [],
   "source": [
    "for member in members:\n",
    "    # We want our workers to ALWAYS \"report back\" to the supervisor when done\n",
    "    workflow.add_edge(member, \"supervisor\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "12a6e5d6-d024-406b-8975-5780bc090497",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:38:11.714802Z",
     "iopub.status.busy": "2024-09-11T15:38:11.714175Z",
     "iopub.status.idle": "2024-09-11T15:38:11.724613Z",
     "shell.execute_reply": "2024-09-11T15:38:11.722762Z",
     "shell.execute_reply.started": "2024-09-11T15:38:11.714749Z"
    }
   },
   "outputs": [],
   "source": [
    "# The supervisor populates the \"next\" field in the graph state\n",
    "# which routes to a node or finishes\n",
    "conditional_map = {k: k for k in members}\n",
    "conditional_map[\"FINISH\"] = END\n",
    "workflow.add_conditional_edges(\"supervisor\", lambda x: x[\"next\"], conditional_map)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "ffd0c1d9-d5bf-475e-934e-616141eaab49",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:39:25.931365Z",
     "iopub.status.busy": "2024-09-11T15:39:25.930745Z",
     "iopub.status.idle": "2024-09-11T15:39:25.941905Z",
     "shell.execute_reply": "2024-09-11T15:39:25.939903Z",
     "shell.execute_reply.started": "2024-09-11T15:39:25.931312Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'Researcher': 'Researcher', 'Coder': 'Coder', 'FINISH': '__end__'}"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "conditional_map"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "d244d48e-71c8-4d88-8f9f-9d512f9597d0",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:38:16.927485Z",
     "iopub.status.busy": "2024-09-11T15:38:16.926863Z",
     "iopub.status.idle": "2024-09-11T15:38:16.939598Z",
     "shell.execute_reply": "2024-09-11T15:38:16.937696Z",
     "shell.execute_reply.started": "2024-09-11T15:38:16.927435Z"
    }
   },
   "outputs": [],
   "source": [
    "# Finally, add entrypoint\n",
    "workflow.add_edge(START, \"supervisor\")\n",
    "\n",
    "graph = workflow.compile()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "b7c31796-2a46-402b-b16e-954e4bb2d944",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:38:30.270174Z",
     "iopub.status.busy": "2024-09-11T15:38:30.269503Z",
     "iopub.status.idle": "2024-09-11T15:38:32.340499Z",
     "shell.execute_reply": "2024-09-11T15:38:32.338562Z",
     "shell.execute_reply.started": "2024-09-11T15:38:30.270122Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "\n",
    "try:\n",
    "    display(Image(graph.get_graph().draw_mermaid_png()))\n",
    "except Exception:\n",
    "    # This requires some extra dependencies and is optional\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "11871a89-f07e-434a-beaa-65dbc0ecb08f",
   "metadata": {},
   "source": [
    "### Invoke the team"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "83ec08b1-38d0-407b-85fa-bcbced8a186f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:41:05.851116Z",
     "iopub.status.busy": "2024-09-11T15:41:05.850486Z",
     "iopub.status.idle": "2024-09-11T15:41:09.749496Z",
     "shell.execute_reply": "2024-09-11T15:41:09.748274Z",
     "shell.execute_reply.started": "2024-09-11T15:41:05.851063Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'supervisor': {'next': 'Coder'}}\n",
      "----\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Python REPL can execute arbitrary code. Use with caution.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'Coder': {'messages': [HumanMessage(content='The code executed successfully and printed the following output to the terminal:\\n\\n```\\nHello, World!\\n```', name='Coder')]}}\n",
      "----\n",
      "{'supervisor': {'next': 'FINISH'}}\n",
      "----\n"
     ]
    }
   ],
   "source": [
    "for s in graph.stream(\n",
    "    {\n",
    "        \"messages\": [\n",
    "            HumanMessage(content=\"Code hello world and print it to the terminal\")\n",
    "        ]\n",
    "    }\n",
    "):\n",
    "    # if \"__end__\" not in s:\n",
    "    print(s)\n",
    "    print(\"----\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "44285483-801e-4185-9229-625342e2c77e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-11T15:43:08.100957Z",
     "iopub.status.busy": "2024-09-11T15:43:08.100356Z",
     "iopub.status.idle": "2024-09-11T15:43:24.456519Z",
     "shell.execute_reply": "2024-09-11T15:43:24.454681Z",
     "shell.execute_reply.started": "2024-09-11T15:43:08.100905Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'supervisor': {'next': 'Researcher'}}\n",
      "----\n",
      "{'Researcher': {'messages': [HumanMessage(content='### Research Report on Pikas\\n\\n#### Introduction\\nPikas are small, mountain-dwelling mammals of the family Ochotonidae, closely related to rabbits and hares. These creatures are known for their distinctive high-pitched calls and their adaptation to cold, alpine environments. They are primarily found in North America and Asia.\\n\\n#### Habitat and Behavior\\nPikas typically inhabit rocky slopes and talus fields in mountainous regions, where they can find crevices for shelter and abundant vegetation for food. They are active throughout the year and do not hibernate. Instead, they engage in a behavior known as \"haying,\" where they gather and store dried plant materials in caches to sustain themselves through the winter months.\\n\\nPikas are highly sensitive to temperature changes and prefer foraging in cooler conditions, generally below 25°C (77°F). With rising temperatures due to climate change, pikas are forced to move to higher elevations to find suitable habitats, leading to a reduction in their available foraging time.\\n\\n#### Diet\\nPikas primarily consume a variety of plant materials, including grasses, sedges, and forbs. During the summer, they engage in two types of foraging: direct consumption of food and the collection of plants to store in haypiles for winter use. This dual strategy ensures they have a continuous food supply even when vegetation is scarce during colder months.\\n\\n#### Reproduction\\nPika species exhibit different reproductive behaviors, often influenced by their specific habitat and social structure. Most talus-dwelling pikas are monogamous or polygynous. They have multiple breeding seasons during the warmer months, allowing them to produce several litters per year. The young are typically born in nests hidden within the rocky terrain, where they are protected from predators and harsh weather.\\n\\n#### Conservation and Threats\\nPikas are considered an indicator species for climate change effects due to their sensitivity to temperature variations. Recent studies have shown that some pika populations are declining, particularly in areas where temperatures are rising. The American pika, for example, has experienced significant range retractions and local extirpations in lower elevation habitats, which are becoming warmer and drier.\\n\\nConservation efforts are focused on monitoring pika populations and understanding the impacts of climate change on their habitats. Protected areas like Glacier National Park have become crucial for long-term pika monitoring and public education on climate change impacts.\\n\\n#### Conclusion\\nPikas are fascinating creatures with unique behaviors and adaptations that allow them to thrive in harsh alpine environments. However, their future is uncertain due to the ongoing challenges posed by global warming. Continued research and conservation efforts are essential to ensure the survival of these small but significant mammals.', name='Researcher')]}}\n",
      "----\n",
      "{'supervisor': {'next': 'FINISH'}}\n",
      "----\n"
     ]
    }
   ],
   "source": [
    "for s in graph.stream(\n",
    "    {\"messages\": [HumanMessage(content=\"Write a brief research report on pikas.\")]},\n",
    "    {\"recursion_limit\": 10},\n",
    "):\n",
    "    # if \"__end__\" not in s:\n",
    "    print(s)\n",
    "    print(\"----\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "12b54340-5f15-4e2c-b1ff-df507eef796f",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "agent",
   "language": "python",
   "name": "agent"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
